package com.guojiganzhe.subject.common.util;
//分布式 ID 生成工具
public class IdWorkerUtil {
    //工作节点
    private long workerId;
    //数据中心
    private long datacenterId;
    //毫秒内序列号
    private long sequence;
    //时间起始标记点，作为基准
    private long twepoch = 1585644268888L;
    //机器ID所占的位数
    private long workerIdBits = 5L;
    //数据中心ID所占的位数
    private long datacenterIdBits = 5L;
    //毫秒内序列号自增位
    private long sequenceBits = 12L;
    //毫秒内序列号最大值
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    //数据中心ID最大值
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    //时间戳左移动位
    private long workerIdShift = sequenceBits;
    //数据中心左移动位
    private long datacenterIdShift = sequenceBits + workerIdBits;
    //时间戳左移动数
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    //毫秒内序列号掩码
    private long sequenceMask = -1L ^ (-1L << sequenceBits);
    //上次时间戳
    private long lastTimestamp = -1L;
    //工作节点
    public long getWorkerId() {
        return workerId;
    }
    //数据中心
    public long getDatacenterId() {
        return datacenterId;
    }
    //当前时间戳
    public long getTimestamp() {
        return System.currentTimeMillis();
    }
    //构造函数，初始化
    public IdWorkerUtil(long workerId, long datacenterId, long sequence) {
        if (workerId > maxWorkerId || workerId < 0) {// 校验工作节点ID合法性（必须在0-31之间）
            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {// 校验数据中心ID合法性（必须在0-31之间）

            throw new IllegalArgumentException(
                    String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;// 初始序列号（通常从0开始）
    }
    //生成唯一 ID 的核心方法
    public synchronized long nextId() {
        long timestamp = timeGen();// 1. 获取当前时间戳（毫秒级）
        if (timestamp < lastTimestamp) {// 2. 处理时钟回拨问题（若当前时间戳小于上次生成ID的时间戳，说明时钟回拨）
            System.err.printf(
                    "clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
            throw new RuntimeException(
                    String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
                            lastTimestamp - timestamp));
        }
        if (lastTimestamp == timestamp) {// 3. 处理同一毫秒内的序列号
            sequence = (sequence + 1) & sequenceMask;// 同一毫秒内，序列号+1，并用掩码限制范围（0-4095）
            if (sequence == 0) {// 若序列号溢出（达到4096），则等待至下一毫秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;// 不同毫秒，序列号重置为0
        }
        lastTimestamp = timestamp;// 4. 更新上次生成ID的时间戳
        // 5. 组合各部分生成最终ID
        return ((timestamp - twepoch) << timestampLeftShift)// 时间戳差值部分
                | (datacenterId << datacenterIdShift)       // 数据中心ID部分
                | (workerId << workerIdShift)               // 工作节点ID部分
                | sequence;                                 // 序列号部分
    }
    //当序列号溢出时，循环等待至下一毫秒（确保时间戳递增）
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    //获取当前时间戳
    private long timeGen() {
        return System.currentTimeMillis();
    }

}